public class Test {
    Counter counter = new Counter();

    public static void main(String[] args) throws Exception {
        new Test().execute();
    }

    public void execute() throws Exception {
        Thread t1 = new Thread(() -> {
            for (int i = -10000; i < 0; i++) {
                counter.add(1);
            }
        });

        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 10000; i++) {
                counter.dec(1);
            }
        });

        t1.start();
        t2.start();
        t1.join();
        t2.join();

        System.out.println("Final count: " + counter.get());
    }
}

class Counter {
    private int count = 0;

    public synchronized void add(int n) {
        if (n < 0) {
            // JVM允许同一个线程重复获取同一个锁，这种能被同一个线程反复获取的锁，就叫做可重入锁。
            // 由于Java的线程锁是可重入锁，所以，获取锁的时候，不但要判断是否是第一次获取，还要记录这是第几次获取。
            // 每获取一次锁，记录+1，每退出synchronized块，记录-1，减到0的时候，才会真正释放锁
            dec(-n);
        } else {
            count += n;
        }
    }

    public synchronized void dec(int n) {
        count += n;
    }

    public int get() {
        return count;
    }
}

class Lock {
    final Object lockA = new Object();
    final Object lockB = new Object();

    int value = 0;
    int another = 0;

    public void add(int m) {
        synchronized (lockA) { // 获得lockA的锁
            this.value += m;
            synchronized (lockB) { // 获得lockB的锁
                this.another += m;
            } // 释放lockB的锁
        } // 释放lockA的锁
    }

    public void dec(int m) {
        synchronized (lockB) { // 获得lockB的锁
            this.another -= m;
            synchronized (lockA) { // 获得lockA的锁
                this.value -= m;
            } // 释放lockA的锁
        } // 释放lockB的锁
    }

    // 在获取多个锁的时候，不同线程获取多个不同对象的锁可能导致死锁。
    // 对于上述代码，线程1和线程2如果分别执行add()和dec()方法时：
    // 线程1：进入add()，获得lockA；
    // 线程2：进入dec()，获得lockB。
    // 随后：
    // 线程1：准备获得lockB，失败，等待中；
    // 线程2：准备获得lockA，失败，等待中。

    // 那么我们应该如何避免死锁呢？答案是：线程获取锁的顺序要一致。即严格按照先获取lockA，再获取lockB的顺序，改写dec()方法如下：

    public void dec2(int m) {
        synchronized (lockA) { // 获得lockA的锁
            this.value -= m;
            synchronized (lockB) { // 获得lockB的锁
                this.another -= m;
            } // 释放lockB的锁
        } // 释放lockA的锁
    }
}